home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1994 February: Tool Chest / Dev.CD Feb 94.toast / Tool Chest / Development Platforms / AppsToGo / AppsToGo.src / DTS.Lib / TreeObj.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-06-18  |  59.7 KB  |  2,399 lines  |  [TEXT/MPS ]

  1. /*
  2. ** Apple Macintosh Developer Technical Support
  3. **
  4. ** Program:     DTS.Lib
  5. ** File:        TreeObj.c
  6. ** Written by:  Eric Soldan
  7. **
  8. ** Copyright © 1992-1993 Apple Computer, Inc.
  9. ** All rights reserved.
  10. */
  11.  
  12. /* You may incorporate this sample code into your applications without
  13. ** restriction, though the sample code has been provided "AS IS" and the
  14. ** responsibility for its operation is 100% yours.  However, what you are
  15. ** not permitted to do is to redistribute the source as "DSC Sample Code"
  16. ** after having made changes. If you're going to re-distribute the source,
  17. ** we require that you make it clear in the source that the code was
  18. ** descended from Apple Sample Code, but that you've made changes. */
  19.  
  20. #include "DTS.Lib2.h"
  21. #include "DTS.Lib.protos.h"
  22.  
  23. #ifndef __FILES__
  24. #include <Files.h>
  25. #endif
  26.  
  27. #ifndef __STDIO__
  28. #include <StdIO.h>
  29. #endif
  30.  
  31. #ifndef __STRING__
  32. #include <String.h>
  33. #endif
  34.  
  35. #ifndef __TREEOBJ__
  36. #include "TreeObj.h"
  37. #endif
  38.  
  39.  
  40.  
  41. #define NEW_CHILD     1
  42. #define DISPOSE_CHILD 2
  43. #define MOVE_CHILD    3
  44. #define SWAP_CHILDREN 4
  45. #define CHANGE_CHILD  5
  46.  
  47.  
  48.  
  49. extern TreeObjProcPtr    gTreeObjMethods[];
  50. extern long                gMinTreeObjSize[];
  51.  
  52. static TreeObjHndl    CopyOneChild(TreeObjHndl chndl, TreeObjHndl copyToHndl, short copyCNum);
  53. static OSErr        ReadBranch(TreeObjHndl hndl, short fileRefNum);
  54. static OSErr        ReadTreeObjHeader(TreeObjHndl hndl, short fileRefNum);
  55. static OSErr        WriteTreeObjHeader(TreeObjHndl hndl, short fileRefNum);
  56.  
  57. static TreeObjHndl    GetUndoTaskHndl(TreeObjHndl undo, short editType);
  58. static TreeObjHndl    NewUndoPart(TreeObjHndl taskHndl, short action, TreeObjHndl shndl, short cnum, TreeObjHndl dhndl, short dcnum, Boolean deepCopy);
  59.  
  60. static OSErr        PostNewChild(short editType, TreeObjHndl phndl, short cnum);
  61. static OSErr        PostDisposeChild(short editType, TreeObjHndl phndl, short cnum);
  62. static void            PostMoveChild(short editType, TreeObjHndl shndl, short scnum, TreeObjHndl dhndl, short dcnum);
  63. static void            PostSwapChildren(short editType, TreeObjHndl hndla, short cnuma, TreeObjHndl hndlb, short cnumb);
  64.  
  65. static void            UndoNewChild(TreeObjHndl undoPart);
  66. static void            UndoDisposeChild(TreeObjHndl undoPart);
  67. static void            UndoMoveChild(TreeObjHndl undoPart);
  68. static void            UndoModifyChild(TreeObjHndl undoPart);
  69. static void            UndoModifyChildren(TreeObjHndl dhndl, TreeObjHndl uhndl, Boolean deepCopy);
  70. static void            UndoSwapChildren(TreeObjHndl undoPart);
  71.  
  72.  
  73.  
  74. /**********************************************************************/
  75.  
  76.  
  77.  
  78. /* Creates an object with no parent.  This object will be the root object
  79. ** for a tree.  Returns nil upon failure. */
  80.  
  81. #pragma segment TreeObj
  82. TreeObjHndl    NewRootObj(short ctype, long size)
  83. {
  84.     TreeObjHndl        root;
  85.     TreeObjPtr        ptr;
  86.     char            *cptr;
  87.     short            i;
  88.     TreeObjProcPtr    proc;
  89.  
  90.     if (size < gMinTreeObjSize[ctype])
  91.         size = gMinTreeObjSize[ctype];
  92.             /* Ensure a minimally-sized object. */
  93.  
  94.     root = (TreeObjHndl)NewHandle(sizeof(TreeObj) + size);
  95.     if (!root) return(nil);
  96.         /* Oh well...
  97.         ** Note that since we are creating an orphan, it isn't possible to purge
  98.         ** old undos to possibly make space.  This means that for anyone calling
  99.         ** NewRootObj to get a handlewho considers it okay to purge old undos to
  100.         ** get the memory if necessary, has to do the following:
  101.         ** 1)  Call NewRootObj
  102.         ** 2)  If failure, call PurgeUndo for a particular document to purge the
  103.         **     oldest undo for that document to free up ram.
  104.         ** 3)  If PurgeUndo returns true, then something was purged and there is more
  105.         **     ram available.  Loop back to step 1 and try to create the object again.
  106.         ** 4)  If PurgeUndo returns false, then there really isn't any more ram.  One
  107.         **     possibility is to try purging undos from other documents, but this is
  108.         **     probably rude. */
  109.  
  110.     cptr = GetDataPtr(root);
  111.     for (i = 0; i < size; ++i) cptr[i] = 0;
  112.         /* Initialize data area to 0's.  This is very nice of us. */
  113.  
  114.     ptr = *root;                     /* Deref root object once.      */
  115.     ptr->type        = ctype;         /* Flag what object type it is. */
  116.     ptr->numChildren = 0;            /* Give root 0 children.        */
  117.     ptr->dataSize    = size;        /* Remember the data area size. */
  118.     ptr->treeID      = 0;            /* Requisite do-nothing field.  */
  119.     ptr->parent      = nil;            /* We aren't owned yet.         */
  120.  
  121.     if (proc = gTreeObjMethods[ctype]) {                /* If this object type has a proc...   */
  122.         if ((*proc)(root, INITMESSAGE, CREATEINIT)) {    /* Call the proc with an init message. */
  123.             DisposeHandle((Handle)root);                /* If the init complains, it's no go.  */
  124.             return(nil);
  125.         }
  126.     }
  127.  
  128.     return(root);    /* Success.  Return the handle. */
  129. }
  130.  
  131.  
  132.  
  133. /**********************************************************************/
  134.  
  135.  
  136.  
  137. /* Creates a child of the specified type and adds it to the parent at the
  138. ** specified location.  Returns nil upon failure. */
  139.  
  140. #pragma segment TreeObj
  141. TreeObjHndl    NewChild(short editType, TreeObjHndl phndl, short cnum, short ctype, long size)
  142. {
  143.     TreeObjHndl        chndl;
  144.     TreeObjProcPtr    proc;
  145.  
  146.     for (;;) {
  147.         chndl = NewRootObj(ctype, size);    /* Let somebody else do the creative work. */
  148.         if (chndl) break;                    /* We succeeded at creating an orphan. */
  149.         if (!PurgeUndo(phndl)) return(nil);
  150.             /* Oh well...
  151.             ** There is really no memory.  We even purged the undos to try to make
  152.             ** room, but we still didn't have enough ram to create the orphan object. */
  153.     };
  154.  
  155.     /* Now that we have successfully created an orphan object, make it someone's child. */
  156.  
  157.     GetChildHndlPtr(phndl, &cnum, 1);
  158.         /* Adjust cnum to within bounds.  See GetChildHndlPtr() for more info. */
  159.  
  160.     if (InsertChildHndl(phndl, chndl, cnum)) {
  161.             /* Couldn't insert the child into the parent handle table. */
  162.         if (proc = gTreeObjMethods[ctype])
  163.             (*proc)(chndl, FREEMESSAGE, 0);
  164.                 /* Since we couldn't attatch, call the object with a free message so that
  165.                 ** it can get rid of any additional memory that was allocated when an
  166.                 ** init message was sent to it. */
  167.         DisposeHandle((Handle)chndl);
  168.             /* After the free message, the object now consists of a single handle.
  169.             ** Dispose this handle and it is history completely. */
  170.         return(nil);
  171.     }
  172.  
  173.     if (PostNewChild(editType, phndl, cnum)) {
  174.         DisposeChild(NO_EDIT, phndl, cnum);
  175.         chndl = nil;
  176.     }
  177.  
  178.     DoTreeObjMethod(chndl, UNDOMESSAGE, UNDOTODOC);
  179.     return(chndl);
  180. }
  181.  
  182.  
  183.  
  184. /**********************************************************************/
  185.  
  186.  
  187.  
  188. /* Disposes of the specified child and all offspring of that child. */
  189.  
  190. #pragma segment TreeObj
  191. void    DisposeChild(short editType, TreeObjHndl phndl, short cnum)
  192. {
  193.     TreeObjHndl    chndl;
  194.  
  195.     if (GetChildHndlPtr(phndl, &cnum, 0)) {
  196.         /* This test checks that there are children in the table, plus it
  197.         ** also adjusts cnum to legit values, if possible. */
  198.  
  199.         chndl = GetChildHndl(phndl, cnum);
  200.  
  201.         if (editType) {
  202.             if (PostDisposeChild(editType, phndl, cnum)) {
  203.                     /* Posting a DisposeChild can actually take more ram.
  204.                     ** The reason for this is that the object isn't disposed of.
  205.                     ** It is moved into the undo, plus information as to where to
  206.                     ** put it back is kept.  If PostDisposeChild fails, then we
  207.                     ** are in a bad way memory-wise, so we really need to let the
  208.                     ** dispose occur.  To accomplish this, we disable undos for
  209.                     ** this document, and then we go ahead and allow a straight
  210.                     ** dispose of the child.  This will get the job done, plus it
  211.                     ** will free up some ram.
  212.                     ** The reason that undos are disabled is that the edit that
  213.                     ** is occuring may have multiple operations.  We want to stop
  214.                     ** collection of the remaining operations for this edit, as
  215.                     ** it wouldn't be a complete undo anyway.  The undos will
  216.                     ** be enabled again when NewUndo is called to start a new edit. */
  217.                 DisableUndo(phndl);
  218.                     /* Undo collection off temporarily, plus all undos are purged,
  219.                     ** thus freeing up ram. */
  220.                 editType = 0;
  221.             }
  222.         }
  223.  
  224.         if (!editType) {
  225.             DoTreeObjMethod(chndl, UNDOMESSAGE, UNDOFROMDOC);
  226.                 /* If the dispose was successfully posted, then the object got a message that
  227.                 ** it was leaving the document via PostDisposeChild calling MoveChild.  The
  228.                 ** only way that PostDisposeChild can fail (and therefore not call MoveChild)
  229.                 ** is if an undoTask couldn't be created, and this is prior to MoveChild being
  230.                 ** called.  What all this means is that if we are here, the object hadn't yet
  231.                 ** received a message about leaving the document. */
  232.             DisposeObjAndOffspring(GetChildHndl(phndl, cnum));
  233.             *GetChildHndlPtr(phndl, &cnum, 0) = nil;
  234.                 /* Since the child no longer exists, DeleteChildHndl can't set
  235.                 ** the parent reference for the child to nil.  Setting the reference
  236.                 ** to the child prevents DeleteChildHndl from doing this. */
  237.             DeleteChildHndl(phndl, cnum);
  238.         }
  239.     }
  240. }
  241.  
  242.  
  243.  
  244. /**********************************************************************/
  245.  
  246.  
  247.  
  248. /* Copies the specified child (and all offspring of that child if deepCopy is true). */
  249.  
  250. #pragma segment TreeObj
  251. TreeObjHndl    CopyChild(short editType, TreeObjHndl shndl, short scnum,
  252.                       TreeObjHndl dhndl, short dcnum, Boolean deepCopy)
  253. {
  254.     TreeObjHndl    chndl, copyHndl;
  255.  
  256.     if (!GetChildHndlPtr(shndl, &scnum, 0)) return(nil);
  257.         /* Adjust scnum to within bounds, if possible. */
  258.  
  259.     GetChildHndlPtr(dhndl, &dcnum, 1);
  260.         /* Adjust dcnum to within bounds. */
  261.  
  262.     copyHndl = CopyOneChild(chndl = GetChildHndl(shndl, scnum), dhndl, dcnum);
  263.     if (!copyHndl) return(nil);
  264.  
  265.     if (deepCopy) {
  266.         if (CopyChildren(chndl, copyHndl)) {        /* Copy child's children. */
  267.             DisposeChild(NO_EDIT, dhndl, dcnum);
  268.             return(nil);
  269.         }
  270.     }
  271.  
  272.     if (PostNewChild(editType, dhndl, dcnum)) {
  273.         DisposeChild(NO_EDIT, dhndl, dcnum);
  274.         return(nil);
  275.     }
  276.  
  277.     DoTreeObjMethod(copyHndl, UNDOMESSAGE, UNDOTODOC);
  278.     return(copyHndl);
  279. }
  280.  
  281.  
  282.  
  283. /**********************************************************************/
  284.  
  285.  
  286.  
  287. /* Moves a child from one location on the tree to another. */
  288.  
  289. #pragma segment TreeObj
  290. OSErr    MoveChild(short editType, TreeObjHndl shndl, short scnum, TreeObjHndl dhndl, short dcnum)
  291. {
  292.     TreeObjHndl    chndl;
  293.  
  294.     if (!GetChildHndlPtr(shndl, &scnum, 0)) return(paramErr);
  295.         /* Adjust scnum to within bounds.  Adjustment not possible if no children. */
  296.  
  297.     GetChildHndlPtr(dhndl, &dcnum, 1);        /* Adjust dcnum to within bounds. */
  298.  
  299.     chndl = GetChildHndl(shndl, scnum);
  300.     DoTreeObjMethod(chndl, UNDOMESSAGE, UNDOFROMDOC);
  301.     DeleteChildHndl(shndl, scnum);
  302.  
  303.     GetChildHndlPtr(dhndl, &dcnum, 1);
  304.         /* Adjust dcnum to within bounds.  It may be just out of bounds if shndl
  305.         ** and dhndl are the same.  After the DeleteChildHndl, dcnum may be 2 past
  306.         ** the end, instead of the allowable 1 for a target. */
  307.  
  308.     InsertChildHndl(dhndl, chndl, dcnum);
  309.     DoTreeObjMethod(chndl, UNDOMESSAGE, UNDOTODOC);
  310.  
  311.     PostMoveChild(editType, shndl, scnum, dhndl, dcnum);
  312.     return(noErr);
  313. }
  314.  
  315.  
  316.  
  317. /**********************************************************************/
  318.  
  319.  
  320.  
  321. /* Saves a copy of the child in the undo hierarchy for undo/redo purposes. */
  322.  
  323. #pragma segment TreeObj
  324. OSErr    ModifyChild(short editType, TreeObjHndl phndl, short cnum, Boolean deepCopy)
  325. {
  326.     TreeObjHndl    undo, task, part, copy;
  327.     short        i;
  328.     OSErr        err;
  329.  
  330.     if (!editType) return(noErr);
  331.  
  332.     err  = noErr;
  333.     copy = nil;
  334.     undo = GetUndoHndl(phndl);
  335.  
  336.     if (mDerefUndo(undo)->disabled)
  337.         copy = phndl;        /* Used for flag purposes. */
  338.  
  339.     else {
  340.         if (task = GetUndoTaskHndl(undo, editType)) {
  341.             for (i = (*task)->numChildren; i;) {
  342.                 part = GetChildHndl(task, --i);
  343.                 if (mDerefUndoPart(part)->actionType == CHANGE_CHILD)
  344.                     if ((mDerefUndoPart(part)->shndl ==phndl) && (mDerefUndoPart(part)->scnum ==cnum))
  345.                         return(noErr);
  346.                             /* Child is already posted as changed for this undo editType. */
  347.             }
  348.  
  349.             part = NewUndoPart(task, CHANGE_CHILD, phndl, cnum, nil, 0, deepCopy);
  350.             if (part)
  351.                 copy = CopyChild(NO_EDIT, phndl, cnum, part, -1, deepCopy);
  352.         }
  353.     }
  354.  
  355.     if (!copy) {
  356.         DisableUndo(phndl);
  357.         err = memFullErr;
  358.     }
  359.  
  360.     return(err);
  361. }
  362.  
  363.  
  364.  
  365. /**********************************************************************/
  366.  
  367.  
  368.  
  369. /* Swaps the child handles. */
  370.  
  371. #pragma segment TreeObj
  372. OSErr    SwapChildren(short editType, TreeObjHndl hndla, short cnuma, TreeObjHndl hndlb, short cnumb)
  373. {
  374.     TreeObjHndl    *tptra, *tptrb, hndl;
  375.  
  376.     if (!(tptra = GetChildHndlPtr(hndla, &cnuma, 0))) return(paramErr);
  377.     if (!(tptrb = GetChildHndlPtr(hndlb, &cnumb, 0))) return(paramErr);
  378.  
  379.     hndl = *tptra;
  380.     *tptra = *tptrb;
  381.     *tptrb = hndl;
  382.  
  383.     (*GetChildHndl(hndla, cnuma))->parent = hndla;
  384.     (*GetChildHndl(hndlb, cnumb))->parent = hndlb;
  385.  
  386.     PostSwapChildren(editType, hndla, cnuma, hndlb, cnumb);
  387.     return(noErr);
  388. }
  389.  
  390.  
  391.  
  392. /**********************************************************************/
  393.  
  394.  
  395.  
  396. /* Swaps the child data without swapping the handles. */
  397.  
  398. #pragma segment TreeObj
  399. OSErr    SwapTreeObjData(TreeObjHndl hndla, TreeObjHndl hndlb)
  400. {
  401.     TreeObjHndl    hndl;
  402.     long        size, sizea, sizeb, i;
  403.     char        *ptra, *ptrb, c;
  404.     OSErr        err;
  405.  
  406.     sizea = (*hndla)->dataSize;
  407.     sizeb = (*hndlb)->dataSize;
  408.     if (sizea > sizeb) {        /* Make hndla the small one and hndlb the big one. */
  409.         size  = sizea;
  410.         sizea = sizeb;
  411.         sizeb = size;
  412.         hndl  = hndla;
  413.         hndla = hndlb;
  414.         hndlb = hndl;
  415.     }
  416.  
  417.     err = SetDataSize(hndla, sizeb);        /* Make the small one big. */
  418.     if (!err) {
  419.         ptra = GetDataPtr(hndla);
  420.         ptrb = GetDataPtr(hndlb);
  421.         for (i = 0; i < sizeb; ++i) {
  422.             c = ptra[i];
  423.             ptra[i] = ptrb[i];
  424.             ptrb[i] = c;
  425.         }
  426.         SetDataSize(hndlb, sizea);            /* Make the big one small. */
  427.     }
  428.  
  429.     return(err);
  430. }
  431.  
  432.  
  433.  
  434. /**********************************************************************/
  435. /**********************************************************************/
  436.  
  437.  
  438.  
  439. /* This disposes of the child and any offspring of that child.  It does not remove
  440. ** the child from the parent's child handle table. */
  441.  
  442. #pragma segment TreeObj
  443. void    DisposeObjAndOffspring(TreeObjHndl chndl)
  444. {
  445.     short            nc;
  446.     TreeObjProcPtr    proc;
  447.  
  448.     if (!chndl) return;
  449.  
  450.     if (proc = gTreeObjMethods[(*chndl)->type])
  451.         (*proc)(chndl, FREEMESSAGE, 0);
  452.             /* If the object has any additional deallocation to do, let it do it. */
  453.  
  454.     while (nc = (*chndl)->numChildren) {
  455.         DisposeObjAndOffspring(GetChildHndl(chndl, nc - 1));
  456.         (*chndl)->numChildren--;
  457.     }
  458.     DisposeHandle((Handle)chndl);
  459. }
  460.  
  461.  
  462.  
  463. /**********************************************************************/
  464.  
  465.  
  466.  
  467. /* Copies the children (and children below that and so on) of one object to
  468. ** another object.  Used internally by CopyChild for deep copies. */
  469.  
  470. #pragma segment TreeObj
  471. OSErr    CopyChildren(TreeObjHndl shndl, TreeObjHndl dhndl)
  472. {
  473.     TreeObjHndl    chndl, copyHndl;
  474.     short    i;
  475.     OSErr    err;
  476.  
  477.     for (i = (*shndl)->numChildren; i;) {
  478.         chndl    = GetChildHndl(shndl, --i);
  479.         copyHndl = CopyOneChild(chndl, dhndl, 0);
  480.         if (!copyHndl) return(memFullErr);
  481.         if (err = CopyChildren(chndl, copyHndl)) return(err);
  482.     }
  483.     return(noErr);
  484. }
  485.  
  486.  
  487.  
  488. /**********************************************************************/
  489.  
  490.  
  491.  
  492. /* Used internally by CopyChild for shallow copies. */
  493.  
  494. #pragma segment TreeObj
  495. TreeObjHndl    CopyOneChild(TreeObjHndl chndl, TreeObjHndl copyToHndl, short copycnum)
  496. {
  497.     TreeObjHndl        copyHndl;
  498.     short            type;
  499.     long            size;
  500.     TreeObjProcPtr    proc;
  501.  
  502.     type = (*chndl)->type;
  503.     size = (*chndl)->dataSize;
  504.  
  505.  
  506.     proc = gTreeObjMethods[type];    /* Prevent NewChild() from calling the */
  507.     gTreeObjMethods[type] = nil;    /* object for INITMESSAGE. */
  508.  
  509.     copyHndl = NewChild(NO_EDIT, copyToHndl, copycnum, type, size);
  510.         /* NewChild takes care of bounds-checking for copycnum.  The child data has
  511.         ** not been fully initialized, as we prevented the INITMESSAGE. */
  512.  
  513.     gTreeObjMethods[type] = proc;    /* Re-enable messaging the object. */
  514.  
  515.     if (!copyHndl) return(nil);
  516.  
  517.     BlockMove(GetDataPtr(chndl), GetDataPtr(copyHndl), size);
  518.     if (proc) {
  519.         if ((*proc)(copyHndl, COPYMESSAGE, (long)chndl)) {
  520.             DisposeChild(NO_EDIT, copyHndl, copycnum);
  521.             return(nil);
  522.         }
  523.     }
  524.  
  525.     return(copyHndl);
  526. }
  527.  
  528.  
  529.  
  530. /**********************************************************************/
  531. /**********************************************************************/
  532.  
  533.  
  534.  
  535. /* Adds an existing child to a parent's child handle table. */
  536.  
  537. #pragma segment TreeObj
  538. OSErr    InsertChildHndl(TreeObjHndl phndl, TreeObjHndl chndl, short cnum)
  539. {
  540.     TreeObjHndl    *tptr;
  541.     TreeObjPtr    ptr;
  542.     long        oldSize, newSize, dhSize;
  543.     long        oldTblSize, tblOffset;
  544.     OSErr        err;
  545.  
  546.     oldSize = GetHandleSize((Handle)phndl);
  547.     dhSize  = sizeof(TreeObj) + (ptr = *phndl)->dataSize;        /* Data + header size. */
  548.  
  549.     oldTblSize = ptr->numChildren * sizeof(TreeObjHndl);
  550.  
  551.     newSize = dhSize + oldTblSize + sizeof(TreeObjHndl);
  552.     newSize |= 0x1F;
  553.     ++newSize;
  554.  
  555.     if (newSize > oldSize) {
  556.         SetHandleSize((Handle)phndl, newSize);
  557.         if (err = MemError()) return(err);
  558.         ptr = *phndl;
  559.     }
  560.  
  561.     tptr      = GetChildHndlPtr(phndl, &cnum, 1);
  562.     tblOffset = cnum * sizeof(TreeObjHndl);
  563.     BlockMove((char *)tptr, (char *)(tptr + 1), oldTblSize - tblOffset);
  564.  
  565.     ptr->numChildren++;
  566.     *tptr = chndl;
  567.     (*chndl)->parent = phndl;
  568.     return(noErr);
  569. }
  570.  
  571.  
  572.  
  573. /**********************************************************************/
  574.  
  575.  
  576.  
  577. /* Removes a child from the parent's child handle table. */
  578.  
  579. #pragma segment TreeObj
  580. void    DeleteChildHndl(TreeObjHndl phndl, short cnum)
  581. {
  582.     TreeObjHndl    *tptr;
  583.     TreeObjHndl    chndl;
  584.     TreeObjPtr    ptr;
  585.     long        dhSize, tblSize, tblOffset;
  586.  
  587.     if (!(tptr = GetChildHndlPtr(phndl, &cnum, 0))) return;
  588.     chndl = *tptr;
  589.  
  590.     dhSize    = sizeof(TreeObj) + (ptr = *phndl)->dataSize;
  591.     tblSize   = ptr->numChildren * sizeof(TreeObjHndl);
  592.     tblOffset = cnum * sizeof(TreeObjHndl);
  593.  
  594.     BlockMove((char *)(tptr + 1), (char *)tptr, tblSize - tblOffset - sizeof(TreeObjHndl));
  595.     SetHandleSize((Handle)phndl, ((dhSize + tblSize - sizeof(TreeObjHndl)) | 0x1F) + 1);
  596.     (*phndl)->numChildren--;
  597.  
  598.     if (chndl)
  599.         (*chndl)->parent = nil;
  600. }
  601.  
  602.  
  603.  
  604. /**********************************************************************/
  605.  
  606.  
  607.  
  608. /* Given an object handle, return the root handle. */
  609.  
  610. #pragma segment TreeObj
  611. TreeObjHndl    GetRootHndl(TreeObjHndl hndl)
  612. {
  613.     for (; (*hndl)->parent; hndl = (*hndl)->parent);
  614.     return(hndl);
  615. }
  616.  
  617.  
  618.  
  619. /**********************************************************************/
  620.  
  621.  
  622.  
  623. /* Given a parent handle and a child number, this returns the child handle. */
  624.  
  625. #pragma segment TreeObj
  626. TreeObjHndl    GetChildHndl(TreeObjHndl phndl, short cnum)
  627. {
  628.     TreeObjHndl    *tptr;
  629.  
  630.     if (!(tptr = GetChildHndlPtr(phndl, &cnum, 0))) return(nil);
  631.     return(*tptr);
  632. }
  633.  
  634.  
  635.  
  636. /**********************************************************************/
  637.  
  638.  
  639.  
  640. /* Return a pointer into the child handle table.  This also validates (and corrects)
  641. ** cnum so that it is in range, if possible.  Depending on the usage, pointing to
  642. ** just after the child handle table is either valid or invalid.  If a handle is
  643. ** being added to the table, then pointing just after the table is valid.  If a
  644. ** handle is being removed or referenced, then pointing just after the table is
  645. ** invalid.  the parameter endCase determines which case we are dealing with.  If
  646. ** endCase is 0, then pointing just after the child handle table is invalid, and
  647. ** if the cnum value passed in causes this, then nil is returned for the pointer.
  648. ** if endCase is 1, then pointing just after the child handle table is okay, and
  649. ** therefore nil will never be returned as the pointer.  Any cnum value out of
  650. ** range will be corrected (if possible) to be within range. */
  651.  
  652. #pragma segment TreeObj
  653. TreeObjHndl    *GetChildHndlPtr(TreeObjHndl phndl, short *cnum, short endCase)
  654. {
  655.     TreeObjPtr    ptr;
  656.     short        nc;
  657.     long        dhSize, tblOffset;
  658.  
  659.     ptr = *phndl;
  660.     if (!((nc = ptr->numChildren) + endCase)) return(nil);
  661.  
  662.     if ((*cnum < 0) || (*cnum >= nc + endCase))
  663.         *cnum = nc - 1 + endCase;
  664.  
  665.     dhSize    = sizeof(TreeObj) + ptr->dataSize;
  666.     tblOffset = *cnum * sizeof(TreeObjHndl);
  667.     return((TreeObjHndl *)((char *)ptr + dhSize + tblOffset));
  668. }
  669.  
  670.  
  671.  
  672. /**********************************************************************/
  673.  
  674.  
  675.  
  676. /* Given a child handle, this returns the child number of that
  677. ** handle in the parent's child handle table. */
  678.  
  679. #pragma segment TreeObj
  680. short    GetChildNum(TreeObjHndl hndl)
  681. {
  682.     TreeObjHndl    phndl, *tptr;
  683.     short        nc, j;
  684.  
  685.     if (!(phndl = (*hndl)->parent)) return(-1);
  686.         /* Child doesn't have a parent, and therefore it is a root. */
  687.  
  688.     j = 0;
  689.     if (tptr = GetChildHndlPtr(phndl, &j, 0)) {
  690.         nc  = (*phndl)->numChildren;
  691.         for (; j < nc; ++j) if (*tptr++ == hndl) return(j);
  692.     }
  693.  
  694.     return(-1);
  695. }
  696.  
  697.  
  698.  
  699. /**********************************************************************/
  700. /**********************************************************************/
  701.  
  702.  
  703.  
  704. /* Adjusts the data size, either greater or smaller, depending on the sign of 'delta'. */
  705.  
  706. #pragma segment TreeObj
  707. OSErr    AdjustDataSize(TreeObjHndl hndl, long delta)
  708. {
  709.     TreeObjPtr    ptr;
  710.     long        dhSize, tblSize;
  711.     char        *cptr;
  712.     OSErr        err;
  713.  
  714.     ptr = *hndl;
  715.     tblSize = ptr->numChildren * sizeof (TreeObjHndl);
  716.     dhSize  = sizeof(TreeObj) + ptr->dataSize;
  717.  
  718.     if (!(delta & 0x80000000L)) {
  719.         SetHandleSize((Handle)hndl, ((dhSize + tblSize + delta) | 0x1F) + 1);
  720.         if (err = MemError()) return(err);
  721.         ptr = *hndl;
  722.     }
  723.  
  724.     cptr = (char *)ptr + dhSize;
  725.     BlockMove(cptr, cptr + delta, tblSize);
  726.     ptr->dataSize += delta;
  727.  
  728.     if (delta & 0x80000000L)
  729.         SetHandleSize((Handle)hndl, ((dhSize + tblSize + delta) | 0x1F) + 1);
  730.  
  731.     return(noErr);
  732. }
  733.  
  734.  
  735.  
  736. /**********************************************************************/
  737.  
  738.  
  739.  
  740. /* Sets the data size to newSize. */
  741.  
  742. #pragma segment TreeObj
  743. OSErr    SetDataSize(TreeObjHndl hndl, long newSize)
  744. {
  745.     return(AdjustDataSize(hndl, newSize - (*hndl)->dataSize));
  746. }
  747.  
  748.  
  749.  
  750. /**********************************************************************/
  751.  
  752.  
  753.  
  754. /* Slides some of the data in the data portion of the object starting at
  755. ** 'offset' by a 'delta' amount, either forward or backward, depending
  756. ** on the sign of 'delta'. */
  757.  
  758. #pragma segment TreeObj
  759. OSErr    SlideData(TreeObjHndl hndl, long offset, long delta)
  760. {
  761.     long    dataSize;
  762.     char    *cptr;
  763.     OSErr    err;
  764.  
  765.     dataSize = (*hndl)->dataSize;
  766.  
  767.     if (!(delta & 0x80000000L))
  768.         if (err = AdjustDataSize(hndl, delta)) return(err);
  769.  
  770.     cptr = (char *)GetDataPtr(hndl) + offset;
  771.     BlockMove(cptr, cptr + delta, dataSize - offset);
  772.  
  773.     if (delta & 0x80000000L)
  774.         AdjustDataSize(hndl, delta);
  775.  
  776.     return(noErr);
  777. }
  778.  
  779.  
  780.  
  781. /**********************************************************************/
  782.  
  783.  
  784.  
  785. /* Returns a pointer to the beginning of the object's data area.
  786. ** THIS DOES NOT LOCK THE HANDLE!!  The pointer may become invalid
  787. ** if memory moves. */
  788.  
  789. #pragma segment TreeObj
  790. void    *GetDataPtr(TreeObjHndl hndl)
  791. {
  792.     return(*hndl + 1);
  793. }
  794.  
  795.  
  796.  
  797. /**********************************************************************/
  798. /**********************************************************************/
  799.  
  800.  
  801.  
  802. /* Given an open file that has been previously written via WriteTree, this function
  803. ** is called to read the file data into ram.  The root object for the file is already
  804. ** created by InitDocument.  The file must be open, and the file position must be
  805. ** set to the byte location where the root object of the file starts.  Once this is
  806. ** so, just call this function with a reference to the root object and the open file
  807. ** refrence number. */
  808.  
  809. #pragma segment TreeObj
  810. OSErr    ReadTree(TreeObjHndl hndl, short fileRefNum)
  811. {
  812.     OSErr    err;
  813.  
  814.     if (err = ReadBranch(hndl, fileRefNum)) {
  815.         while ((*hndl)->numChildren)
  816.             DisposeChild(NO_EDIT, hndl, 0);
  817.     }
  818.     else {
  819.         DoNumberTree(hndl);
  820.         DoFTreeMethod(hndl, CONVERTMESSAGE, CONVERTTOHNDL);
  821.     }
  822.  
  823.     return(err);
  824. }
  825.  
  826.  
  827.  
  828. /**********************************************************************/
  829.  
  830.  
  831.  
  832. /* This is an internal function for recursively reading the data from the
  833. ** open file. */
  834.  
  835. #pragma segment TreeObj
  836. OSErr    ReadBranch(TreeObjHndl hndl, short fileRefNum)
  837. {
  838.     TreeObjHndl        chndl;
  839.     TreeObjProcPtr    proc;
  840.     short            numChildren, cnum;
  841.     OSErr            err;
  842.  
  843.     if (err = ReadTreeObjHeader(hndl, fileRefNum)) {
  844.         (*hndl)->numChildren = 0;    /* So we can dispose the portion that was read. */
  845.         return(err);
  846.     }
  847.  
  848.     numChildren = (*hndl)->numChildren;    /* So we can dispose of a partial read if */
  849.     (*hndl)->numChildren = 0;            /* we have a failure after this point.    */
  850.  
  851.     if (proc = gTreeObjMethods[(*hndl)->type])
  852.         err = (*proc)(hndl, FREADMESSAGE, fileRefNum);
  853.     else
  854.         err = ReadTreeObjData(hndl, fileRefNum);
  855.  
  856.     if (err) return(err);
  857.  
  858.     for (cnum = 0; cnum < numChildren; ++cnum) {
  859.         if (!(chndl = NewRootObj(EMPTYOBJ, 0))) return(memFullErr);
  860.         if (InsertChildHndl(hndl, chndl, cnum)) {
  861.             DisposeObjAndOffspring(chndl);
  862.             return(memFullErr);
  863.         }
  864.         if (err = ReadBranch(chndl, fileRefNum)) return(err);
  865.     }
  866.  
  867.     return(noErr);
  868. }
  869.  
  870.  
  871.  
  872. /**********************************************************************/
  873.  
  874.  
  875.  
  876. #pragma segment TreeObj
  877. OSErr    ReadTreeObjHeader(TreeObjHndl hndl, short fileRefNum)
  878. {
  879.     TreeObj        header;
  880.     TreeObjHndl    parent;
  881.     OSErr        err;
  882.     long        count;
  883.  
  884.     count = sizeof(TreeObj) - sizeof(TreeObjHndl);
  885.     if (!(err = FSRead(fileRefNum, &count, &header))) {
  886.         parent = (*hndl)->parent;
  887.         **hndl = header;
  888.         (*hndl)->parent = parent;
  889.     }
  890.  
  891.     return(err);
  892. }
  893.  
  894.  
  895.  
  896. /**********************************************************************/
  897.  
  898.  
  899.  
  900. /* Read in dataSize number of bytes into the object. */
  901.  
  902. #pragma segment TreeObj
  903. OSErr    ReadTreeObjData(TreeObjHndl hndl, short fileRefNum)
  904. {
  905.     long    dataSize;
  906.     OSErr    err;
  907.     char    hstate;
  908.     Ptr        dataPtr;
  909.  
  910.     if (!(err = SetDataSize(hndl, dataSize = (*hndl)->dataSize))) {
  911.         hstate = HGetState((Handle)hndl);
  912.         HLock((Handle)hndl);
  913.         dataPtr = GetDataPtr(hndl);
  914.         err     = FSRead(fileRefNum, &dataSize, dataPtr);
  915.         HSetState((Handle)hndl, hstate);
  916.     }
  917.  
  918.     return(err);
  919. }
  920.  
  921.  
  922.  
  923. /**********************************************************************/
  924.  
  925.  
  926.  
  927. /* Given an open file that is ready to be written to, this function is called to
  928. ** write the file tree to the designated file. */
  929.  
  930. #pragma segment TreeObj
  931. OSErr    WriteTree(TreeObjHndl hndl, short fileRefNum)
  932. {
  933.     TreeObjProcPtr    proc;
  934.     short            cnum;
  935.     OSErr            err;
  936.  
  937.     err = WriteTreeObjHeader(hndl, fileRefNum);
  938.     if (!err) {
  939.  
  940.         DoTreeObjMethod(hndl, CONVERTMESSAGE, CONVERTTOID);
  941.             /* Ready data to be written to file.  Any references to handles are invalid
  942.             ** when written to disk.  These need to be converted to a reference that
  943.             ** makes sense when read in from disk when a file is opened.  The standard
  944.             ** way to do this is to convert the handle reference to a tree-obj-number
  945.             ** reference.  Prior to WriteTree() being called, DoNumberTree() is called.
  946.             ** DoNumberTree() walks the tree and numbers each handle sequentially, thus
  947.             ** giving each handle a unique id number. */
  948.  
  949.         if (proc = gTreeObjMethods[(*hndl)->type])
  950.             err = (*proc)(hndl, FWRITEMESSAGE, fileRefNum);
  951.         else
  952.             err = WriteTreeObjData(hndl, fileRefNum);
  953.  
  954.         DoTreeObjMethod(hndl, CONVERTMESSAGE, CONVERTTOHNDL);
  955.             /* Undo any id references back to handle references. */
  956.     }
  957.  
  958.  
  959.     if (!err) {
  960.         for (cnum = 0; cnum < (*hndl)->numChildren; ++cnum)
  961.             if (err = WriteTree(GetChildHndl(hndl, cnum), fileRefNum)) break;
  962.     }
  963.  
  964.     return(err);
  965. }
  966.  
  967.  
  968.  
  969. /**********************************************************************/
  970.  
  971.  
  972.  
  973. #pragma segment TreeObj
  974. OSErr    WriteTreeObjHeader(TreeObjHndl hndl, short fileRefNum)
  975. {
  976.     TreeObj    header;
  977.     OSErr    err;
  978.     long    count;
  979.  
  980.     header = **hndl;
  981.     count = sizeof(TreeObj) - sizeof(TreeObjHndl);
  982.     err = FSWrite(fileRefNum, &count, &header);
  983.  
  984.     return(err);
  985. }
  986.  
  987.  
  988.  
  989. /**********************************************************************/
  990.  
  991.  
  992.  
  993. /* Write out dataSize number of bytes from the object. */
  994.  
  995. #pragma segment TreeObj
  996. OSErr    WriteTreeObjData(TreeObjHndl hndl, short fileRefNum)
  997. {
  998.     long    dataSize;
  999.     OSErr    err;
  1000.     char    hstate;
  1001.     Ptr        dataPtr;
  1002.  
  1003.     dataSize = (*hndl)->dataSize;
  1004.  
  1005.     hstate  = HGetState((Handle)hndl);
  1006.     HLock((Handle)hndl);
  1007.     dataPtr = GetDataPtr(hndl);
  1008.     err     = FSWrite(fileRefNum, &dataSize, dataPtr);
  1009.     HSetState((Handle)hndl, hstate);
  1010.  
  1011.     return(err);
  1012. }
  1013.  
  1014.  
  1015.  
  1016. /**********************************************************************/
  1017.  
  1018.  
  1019.  
  1020. /* Call the object for each member of the tree (or branch) starting
  1021. ** from the back of the tree working forward. */
  1022.  
  1023. #pragma segment TreeObj
  1024. void    DoBTreeMethod(TreeObjHndl hndl, short message, long data)
  1025. {
  1026.     short    cnum;
  1027.  
  1028.     DoTreeObjMethod(hndl, message, data);
  1029.  
  1030.     for (cnum = (*hndl)->numChildren; cnum;)
  1031.         DoBTreeMethod(GetChildHndl(hndl, --cnum), message, data);
  1032. }
  1033.  
  1034.  
  1035.  
  1036. /**********************************************************************/
  1037.  
  1038.  
  1039.  
  1040. /* Same as DoBTreeMethod, except that an error aborts tree walk and returns error. */
  1041.  
  1042. #pragma segment TreeObj
  1043. OSErr    DoErrBTreeMethod(TreeObjHndl hndl, short message, long data)
  1044. {
  1045.     short    cnum;
  1046.     OSErr    err;
  1047.  
  1048.     if (err = DoTreeObjMethod(hndl, message, data))
  1049.         return(err);
  1050.  
  1051.     for (cnum = (*hndl)->numChildren; cnum;)
  1052.         if (err = DoErrBTreeMethod(GetChildHndl(hndl, --cnum), message, data))
  1053.             return(err);
  1054.  
  1055.     return(noErr);
  1056. }
  1057.  
  1058.  
  1059.  
  1060. /**********************************************************************/
  1061.  
  1062.  
  1063.  
  1064. /* Call the object for each member of the tree (or branch) starting
  1065. ** from the front of the tree working backward. */
  1066.  
  1067. #pragma segment TreeObj
  1068. void    DoFTreeMethod(TreeObjHndl hndl, short message, long data)
  1069. {
  1070.     short    cnum;
  1071.  
  1072.     DoTreeObjMethod(hndl, message, data);
  1073.  
  1074.     for (cnum = 0; cnum < (*hndl)->numChildren; ++cnum) 
  1075.         DoFTreeMethod(GetChildHndl(hndl, cnum), message, data);
  1076. }
  1077.  
  1078.  
  1079.  
  1080. /**********************************************************************/
  1081.  
  1082.  
  1083.  
  1084. /* Same as DoFTreeMethod, except that an error aborts tree walk and returns error. */
  1085.  
  1086. #pragma segment TreeObj
  1087. OSErr    DoErrFTreeMethod(TreeObjHndl hndl, short message, long data)
  1088. {
  1089.     short    cnum;
  1090.     OSErr    err;
  1091.  
  1092.     if (err = DoTreeObjMethod(hndl, message, data))
  1093.         return(err);
  1094.  
  1095.     for (cnum = 0; cnum < (*hndl)->numChildren; ++cnum) 
  1096.         if (err = DoErrFTreeMethod(GetChildHndl(hndl, cnum), message, data))
  1097.             return(err);
  1098.  
  1099.     return(noErr);
  1100. }
  1101.  
  1102.  
  1103.  
  1104. /**********************************************************************/
  1105.  
  1106.  
  1107.  
  1108. /* If the object has a method procedure, call it.  If no method procedure,
  1109. ** then do nothing. */
  1110.  
  1111. #pragma segment TreeObj
  1112. long    DoTreeObjMethod(TreeObjHndl hndl, short message, long data)
  1113. {
  1114.     TreeObjProcPtr    proc;
  1115.  
  1116.     if (proc = gTreeObjMethods[(*hndl)->type]) return((*proc)(hndl, message, data));
  1117.     return(0);
  1118. }
  1119.  
  1120.  
  1121.  
  1122. /**********************************************************************/
  1123.  
  1124.  
  1125.  
  1126. /* Number each member in the tree with a unique treeID.  The tree is number
  1127. ** sequentially from front to back.  The first treeID number is 1.  0 is
  1128. ** reserved for Hndl2ID/ID2Hndl conversions where it is possible that the
  1129. ** handle value is nil.  The nil handle will convert to 0, and convert back
  1130. ** to nil. */
  1131.  
  1132. #pragma segment TreeObj
  1133. static void    DoNumberTree0(TreeObjHndl hndl);
  1134. void        DoNumberTree(TreeObjHndl hndl)
  1135. {
  1136.     DoNumberTree0(GetRootHndl(hndl));
  1137. }
  1138.  
  1139.  
  1140.  
  1141. /**********************************************************************/
  1142.  
  1143.  
  1144.  
  1145. #pragma segment TreeObj
  1146. void    DoNumberTree0(TreeObjHndl hndl)
  1147. {
  1148.     short            cnum;
  1149.     static short    nodeNum;
  1150.  
  1151.     if (!(*hndl)->parent)
  1152.         nodeNum = 0;
  1153.     (*hndl)->treeID = ++nodeNum;
  1154.  
  1155.     for (cnum = 0; cnum < (*hndl)->numChildren; ++cnum)
  1156.         DoNumberTree0(GetChildHndl(hndl, cnum));
  1157. }
  1158.  
  1159.  
  1160.  
  1161. /**********************************************************************/
  1162.  
  1163.  
  1164.  
  1165. /* This function is used to convert a handle reference into a treeID reference.
  1166. ** A pointer to the handle reference is passed in.  Typical usage will be where
  1167. ** a handle object has a reference to another handle object.  Handle object
  1168. ** references aren't meaningful when saved to disk, and therefore don't persist
  1169. ** in their native form.  These handle references need to be converted into
  1170. ** a treeID reference so that they can be saved.
  1171. ** The tree first needs to be numbered so that the treeID references are unique
  1172. ** and meaningful.  The tree is numbered by first calling DoNumberTree().  It
  1173. ** numbers all the members of the tree hierarchy uniquely and sequentially. */
  1174.  
  1175. #pragma segment TreeObj
  1176. void    Hndl2ID(TreeObjHndl *hndl)
  1177. {
  1178.     if (*hndl)
  1179.         *hndl = (TreeObjHndl)(**hndl)->treeID;
  1180. }
  1181.  
  1182.  
  1183.  
  1184. /**********************************************************************/
  1185.  
  1186.  
  1187.  
  1188. /* Given a tree object ID and a reference object (any member of the tree),
  1189. ** return the cooresponding object handle.  DoNumberTree() must be called
  1190. ** prior to using this function, and after the last change to the tree, as
  1191. ** it generates the object treeID numbers for the entire tree. */
  1192.  
  1193. #pragma segment TreeObj
  1194. void    ID2Hndl(TreeObjHndl refHndl, TreeObjHndl *hndl)
  1195. {
  1196.     short        cnum;
  1197.     TreeObjHndl    chndl;
  1198.  
  1199.     if ((!refHndl) || (!*hndl)) return;
  1200.  
  1201.     refHndl = GetRootHndl(refHndl);
  1202.     for (;;) {
  1203.         if ((*refHndl)->treeID == (long)*hndl) {
  1204.             *hndl = refHndl;
  1205.             return;
  1206.         }
  1207.         if (!(cnum = (*refHndl)->numChildren)) return;
  1208.         for (; cnum;) {
  1209.             chndl = GetChildHndl(refHndl, --cnum);
  1210.             if ((*chndl)->treeID <= (long)*hndl) {
  1211.                 refHndl = chndl;
  1212.                 break;
  1213.             }
  1214.         }
  1215.     }
  1216. }
  1217.  
  1218.  
  1219.  
  1220. /**********************************************************************/
  1221. /**********************************************************************/
  1222. /**********************************************************************/
  1223.  
  1224.  
  1225.  
  1226. /* Given an object handle, return the undo handle. */
  1227.  
  1228. #pragma segment TreeObj
  1229. TreeObjHndl    GetUndoHndl(TreeObjHndl undo)
  1230. {
  1231.     for (; (*undo)->parent; undo = (*undo)->parent);
  1232.     if ((*undo)->type == ROOTOBJ)
  1233.         undo = mDerefRoot(undo)->undo;
  1234.     return(undo);
  1235. }
  1236.  
  1237.  
  1238.  
  1239. /**********************************************************************/
  1240.  
  1241.  
  1242.  
  1243. /* Used to close out an old undo task.  Closing out the old task means that
  1244. ** any editing to the document will be recorded into a new undo task.  Use
  1245. ** this to separate two edits of the same edit type that would otherwise
  1246. ** get recorded into the same undo task. */
  1247.  
  1248. #pragma segment TreeObj
  1249. void    NewUndo(TreeObjHndl hndl)
  1250. {
  1251.     TreeObjHndl    undo;
  1252.  
  1253.     undo = GetUndoHndl(hndl);
  1254.     mDerefUndo(undo)->lastEditType = NO_EDIT;
  1255.     mDerefUndo(undo)->disabled     = false;
  1256. }
  1257.  
  1258.  
  1259.  
  1260. /**********************************************************************/
  1261.  
  1262.  
  1263.  
  1264. /* If an edit fails, it can be backed out of by calling this.  All edits
  1265. ** that were done will be undone, thus recovering the state of the
  1266. ** document prior to the edit. */
  1267.  
  1268. #pragma segment TreeObj
  1269. void    RevertEdit(TreeObjHndl hndl)
  1270. {
  1271.     TreeObjHndl    root;
  1272.     FileRecHndl    frHndl;
  1273.     Boolean        docDirty;
  1274.  
  1275.     root     = GetRootHndl(hndl);
  1276.     frHndl   = mDerefRoot(root)->frHndl;
  1277.     docDirty = (*frHndl)->fileState.docDirty;
  1278.  
  1279.     DoUndoTask(hndl, DOUNDO, false);
  1280.         /* Use the undo mechanism to back out of the edit task. */
  1281.  
  1282.     DisposeChild(NO_EDIT, GetUndoHndl(hndl), -1);
  1283.         /* Get rid of the undo that was just used to revert.
  1284.         ** Leave the rest of the undos. */
  1285.  
  1286.     (*frHndl)->fileState.docDirty = docDirty;
  1287. }
  1288.  
  1289.  
  1290.  
  1291. /**********************************************************************/
  1292.  
  1293.  
  1294.  
  1295. /* Call this to undo or redo editing to the document.  If redo is false,
  1296. ** the it is an undo task. */
  1297.  
  1298. #pragma segment TreeObj
  1299. void    DoUndoTask(TreeObjHndl hndl, Boolean redo, Boolean fixup)
  1300. {
  1301.     FileRecHndl    frHndl;
  1302.     TreeObjHndl    undo, undoTask, undoPart;
  1303.     short        numUndos, undoDepth, undoPartNum;
  1304.     short        beg, end, inc;
  1305.     Point        contOrg;
  1306.  
  1307.     NewUndo(undo = GetUndoHndl(hndl));
  1308.  
  1309.     numUndos  = (*undo)->numChildren;
  1310.     undoDepth = mDerefUndo(undo)->undoDepth;
  1311.  
  1312.     if ((redo) && (numUndos == undoDepth)) return;
  1313.     if (!(numUndos | undoDepth)) return;
  1314.  
  1315.     frHndl = mDerefUndo(undo)->frHndl;
  1316.  
  1317.     if (redo) undoDepth++;
  1318.     undoTask = GetChildHndl(undo, --undoDepth);
  1319.  
  1320.     if (!redo) {
  1321.         GetContentOrigin((*frHndl)->fileState.window, &contOrg);
  1322.         mDerefUndoTask(undoTask)->redoOrigin = contOrg;
  1323.         contOrg = mDerefUndoTask(undoTask)->undoOrigin;
  1324.     }
  1325.     else contOrg = mDerefUndoTask(undoTask)->redoOrigin;
  1326.  
  1327.     if (fixup)
  1328.         DoUndoFixup(frHndl, contOrg, 0);
  1329.             /* Prepare for undo task, such as deselecting the current selection so
  1330.             ** that the undone stuff can be displayed as the only selected stuff. */
  1331.  
  1332.     beg = (*undoTask)->numChildren - 1;
  1333.     end = -1;
  1334.     inc = -1;
  1335.     if (redo) {
  1336.         end = ++beg;
  1337.         beg = 0;
  1338.         inc = 1;
  1339.     }
  1340.  
  1341.     for (undoPartNum = beg; undoPartNum != end; undoPartNum += inc) {
  1342.         undoPart = GetChildHndl(undoTask, undoPartNum);
  1343.         switch(mDerefUndoPart(undoPart)->actionType) {
  1344.             case NEW_CHILD:
  1345.                 UndoNewChild(undoPart);
  1346.                 break;
  1347.             case DISPOSE_CHILD:
  1348.                 UndoDisposeChild(undoPart);
  1349.                 break;
  1350.             case MOVE_CHILD:
  1351.                 UndoMoveChild(undoPart);
  1352.                 break;
  1353.             case CHANGE_CHILD:
  1354.                 UndoModifyChild(undoPart);
  1355.                 break;
  1356.             case SWAP_CHILDREN:
  1357.                 UndoSwapChildren(undoPart);
  1358.                 break;
  1359.         }
  1360.     }
  1361.  
  1362.     inc = -1;
  1363.     if (redo)
  1364.         inc = 1;
  1365.  
  1366.     mDerefUndo(undo)->undoDepth += inc;
  1367.  
  1368.     if (fixup)
  1369.         DoUndoFixup(frHndl, contOrg, 1);
  1370.             /* Clean up and redisplay after undo task. */
  1371.  
  1372.     SetDocDirty(frHndl);
  1373. }
  1374.  
  1375.  
  1376.  
  1377. /**********************************************************************/
  1378.  
  1379.  
  1380.  
  1381. #pragma segment TreeObj
  1382. TreeObjHndl    GetUndoTaskHndl(TreeObjHndl undo, short editType)
  1383. {
  1384.     TreeObjHndl        lastTaskHndl;
  1385.     short            lastEditType, undoDepth, addNewUndo, numUndoLevels, maxNumUndos;
  1386.     Point            contOrg;
  1387.     FileRecHndl        frHndl;
  1388.     WindowPtr        window;
  1389.  
  1390.     if (!(maxNumUndos = mDerefUndo(undo)->maxNumUndos)) return(nil);
  1391.  
  1392.     lastEditType = mDerefUndo(undo)->lastEditType;
  1393.     undoDepth    = mDerefUndo(undo)->undoDepth;
  1394.  
  1395.     addNewUndo = false;
  1396.     if (editType != lastEditType)
  1397.         addNewUndo = true;
  1398.     if (!(numUndoLevels = (*undo)->numChildren))
  1399.         addNewUndo = true;
  1400.  
  1401.     while (undoDepth < numUndoLevels) {
  1402.         DisposeChild(NO_EDIT, undo, --numUndoLevels);
  1403.         addNewUndo = true;        /* Flushing old also indicates a new undo. */
  1404.     }                            /* undoDepth now is the same as numUndoLevels. */
  1405.  
  1406.     lastTaskHndl = nil;
  1407.     if (!addNewUndo) {
  1408.         lastTaskHndl = GetChildHndl(undo, -1);        /* Get last child handle. */
  1409.         if ((editType) && (editType != mDerefUndoTask(lastTaskHndl)->editType))
  1410.             lastTaskHndl = nil;
  1411.     }
  1412.  
  1413.     if (!lastTaskHndl) {
  1414.         while (numUndoLevels >= maxNumUndos) {
  1415.             DisposeChild(NO_EDIT, undo, 0);
  1416.             mDerefUndo(undo)->undoDepth--;
  1417.             numUndoLevels--;
  1418.         }    /* Restrict number of undos to designated level. */
  1419.  
  1420.         if (lastTaskHndl = NewChild(NO_EDIT, undo, numUndoLevels, UNDOTASKOBJ, 0)) {
  1421.             mDerefUndo(undo)->lastEditType = editType;
  1422.             mDerefUndo(undo)->undoDepth++;
  1423.             frHndl = mDerefUndo(undo)->frHndl;
  1424.             window = (*frHndl)->fileState.window;
  1425.             GetContentOrigin(window, &contOrg);
  1426.             mDerefUndoTask(lastTaskHndl)->editType   = editType;
  1427.             mDerefUndoTask(lastTaskHndl)->undoOrigin = contOrg;
  1428.             mDerefUndoTask(lastTaskHndl)->redoOrigin = contOrg;
  1429.         }
  1430.     }
  1431.  
  1432.     return(lastTaskHndl);
  1433. }
  1434.  
  1435.  
  1436.  
  1437. /**********************************************************************/
  1438.  
  1439.  
  1440.  
  1441. #pragma segment TreeObj
  1442. TreeObjHndl    NewUndoPart(TreeObjHndl taskHndl, short action, TreeObjHndl shndl, short scnum,
  1443.                         TreeObjHndl dhndl, short dcnum, Boolean deepCopy)
  1444. {
  1445.     TreeObjHndl    partHndl;
  1446.     UndoPartObj    *partPtr;
  1447.  
  1448.     partHndl = NewChild(NO_EDIT, taskHndl, -1, UNDOPARTOBJ, 0);    /* Add new child to end. */
  1449.     if (partHndl) {
  1450.         partPtr = GetDataPtr(partHndl);
  1451.         partPtr->actionType = action;
  1452.         partPtr->shndl      = shndl;
  1453.         partPtr->scnum      = scnum;
  1454.         partPtr->dhndl      = dhndl;
  1455.         partPtr->dcnum      = dcnum;
  1456.         partPtr->deepCopy   = deepCopy;
  1457.     }
  1458.  
  1459.     return(partHndl);
  1460. }
  1461.  
  1462.  
  1463.  
  1464. /**********************************************************************/
  1465.  
  1466.  
  1467.  
  1468. #pragma segment TreeObj
  1469. OSErr    PostNewChild(short editType, TreeObjHndl phndl, short cnum)
  1470. {
  1471.     TreeObjHndl    undo, task;
  1472.  
  1473.     if (!editType) return(noErr);
  1474.  
  1475.     undo = GetUndoHndl(phndl);
  1476.     if (mDerefUndo(undo)->disabled) return(noErr);
  1477.  
  1478.     if (task = GetUndoTaskHndl(undo, editType))
  1479.         if (NewUndoPart(task, NEW_CHILD, phndl, cnum, nil, 0, false)) return(noErr);
  1480.  
  1481.     return(memFullErr);
  1482. }
  1483.  
  1484.  
  1485.  
  1486. /**********************************************************************/
  1487.  
  1488.  
  1489.  
  1490. #pragma segment TreeObj
  1491. OSErr    PostDisposeChild(short editType, TreeObjHndl phndl, short cnum)
  1492. {
  1493.     TreeObjHndl    undo, task, part;
  1494.  
  1495.     if (!editType) return(noErr);
  1496.  
  1497.     undo = GetUndoHndl(phndl);
  1498.     if (mDerefUndo(undo)->disabled) return(noErr);
  1499.  
  1500.     if (task = GetUndoTaskHndl(undo, editType)) {
  1501.         part = NewUndoPart(task, DISPOSE_CHILD, phndl, cnum, nil, 0, false);
  1502.         if (part) {
  1503.             MoveChild(NO_EDIT, phndl, cnum, part, -1);
  1504.             return(noErr);
  1505.         }
  1506.     }
  1507.  
  1508.     return(memFullErr);
  1509. }
  1510.  
  1511.  
  1512.  
  1513. /**********************************************************************/
  1514.  
  1515.  
  1516.  
  1517. #pragma segment TreeObj
  1518. void    PostMoveChild(short editType, TreeObjHndl shndl, short scnum, TreeObjHndl dhndl, short dcnum)
  1519. {
  1520.     TreeObjHndl    undo, task;
  1521.  
  1522.     if (editType) {
  1523.         undo = GetUndoHndl(shndl);
  1524.         if (!mDerefUndo(undo)->disabled)
  1525.             if (task = GetUndoTaskHndl(undo, editType))
  1526.                 NewUndoPart(task, MOVE_CHILD, shndl, scnum, dhndl, dcnum, false);
  1527.     }
  1528. }
  1529.  
  1530.  
  1531.  
  1532. /**********************************************************************/
  1533.  
  1534.  
  1535.  
  1536. #pragma segment TreeObj
  1537. void    PostSwapChildren(short editType, TreeObjHndl hndla, short cnuma, TreeObjHndl hndlb, short cnumb)
  1538. {
  1539.     TreeObjHndl    undo, task;
  1540.  
  1541.     if (editType) {
  1542.         undo = GetUndoHndl(hndla);
  1543.         if (!mDerefUndo(undo)->disabled)
  1544.             if (task = GetUndoTaskHndl(undo, editType))
  1545.                 NewUndoPart(task, SWAP_CHILDREN, hndla, cnuma, hndlb, cnumb, false);
  1546.  
  1547.     }
  1548. }
  1549.  
  1550.  
  1551.  
  1552. /**********************************************************************/
  1553.  
  1554.  
  1555.  
  1556. #pragma segment TreeObj
  1557. void    UndoNewChild(TreeObjHndl undoPart)
  1558. {
  1559.     TreeObjHndl    shndl, chndl;
  1560.     short        scnum;
  1561.  
  1562.     shndl = mDerefUndoPart(undoPart)->shndl;
  1563.     scnum = mDerefUndoPart(undoPart)->scnum;
  1564.     mDerefUndoPart(undoPart)->actionType = DISPOSE_CHILD;
  1565.  
  1566.     chndl = GetChildHndl(shndl, scnum);
  1567.     MoveChild(NO_EDIT, shndl, scnum, undoPart, 0);
  1568. }
  1569.  
  1570.  
  1571.  
  1572. /**********************************************************************/
  1573.  
  1574.  
  1575.  
  1576. #pragma segment TreeObj
  1577. void    UndoDisposeChild(TreeObjHndl undoPart)
  1578. {
  1579.     TreeObjHndl    shndl, chndl;
  1580.     short        scnum;
  1581.  
  1582.     shndl = mDerefUndoPart(undoPart)->shndl;
  1583.     scnum = mDerefUndoPart(undoPart)->scnum;
  1584.     mDerefUndoPart(undoPart)->actionType = NEW_CHILD;
  1585.  
  1586.     chndl = GetChildHndl(undoPart, 0);
  1587.     MoveChild(NO_EDIT, undoPart, 0, shndl, scnum);
  1588. }
  1589.  
  1590.  
  1591.  
  1592. /**********************************************************************/
  1593.  
  1594.  
  1595.  
  1596. #pragma segment TreeObj
  1597. void    UndoMoveChild(TreeObjHndl undoPart)
  1598. {
  1599.     TreeObjHndl    shndl, dhndl;
  1600.     short        scnum, dcnum;
  1601.  
  1602.     shndl = mDerefUndoPart(undoPart)->shndl;
  1603.     scnum = mDerefUndoPart(undoPart)->scnum;
  1604.     dhndl = mDerefUndoPart(undoPart)->dhndl;
  1605.     dcnum = mDerefUndoPart(undoPart)->dcnum;
  1606.  
  1607.     MoveChild(NO_EDIT, dhndl, dcnum, shndl, scnum);
  1608.  
  1609.     mDerefUndoPart(undoPart)->shndl = dhndl;
  1610.     mDerefUndoPart(undoPart)->scnum = dcnum;
  1611.     mDerefUndoPart(undoPart)->dhndl = shndl;
  1612.     mDerefUndoPart(undoPart)->dcnum = scnum;
  1613. }
  1614.  
  1615.  
  1616.  
  1617. /**********************************************************************/
  1618.  
  1619.  
  1620.  
  1621. #pragma segment TreeObj
  1622. void    UndoModifyChild(TreeObjHndl undoPart)
  1623. {
  1624.     TreeObjHndl    shndl, dchndl, uchndl;
  1625.     short        scnum;
  1626.  
  1627.     shndl = mDerefUndoPart(undoPart)->shndl;
  1628.     scnum = mDerefUndoPart(undoPart)->scnum;
  1629.  
  1630.     dchndl = GetChildHndl(shndl, scnum);    /* Document child handle. */
  1631.     uchndl = GetChildHndl(undoPart, 0);        /* Undo child handle. */
  1632.  
  1633.     UndoModifyChildren(dchndl, uchndl, mDerefUndoPart(undoPart)->deepCopy);
  1634. }
  1635.  
  1636.  
  1637.  
  1638. /**********************************************************************/
  1639.  
  1640.  
  1641.  
  1642. #pragma segment TreeObj
  1643. void    UndoModifyChildren(TreeObjHndl dhndl, TreeObjHndl uhndl, Boolean deepCopy)
  1644. {
  1645.     TreeObjHndl    dchndl, uchndl;
  1646.     short        i;
  1647.  
  1648.     DoTreeObjMethod(dhndl, UNDOMESSAGE, UNDOFROMDOC);    /* Old data leaving document. */
  1649.     SwapTreeObjData(dhndl, uhndl);
  1650.     DoTreeObjMethod(dhndl, UNDOMESSAGE, UNDOTODOC);        /* New data entering document. */
  1651.  
  1652.     if (deepCopy) {
  1653.         for (i = (*dhndl)->numChildren; i;) {
  1654.             dchndl = GetChildHndl(dhndl, --i);
  1655.             uchndl = GetChildHndl(uhndl, i);
  1656.             UndoModifyChildren(dchndl, uchndl, deepCopy);
  1657.         }
  1658.     }
  1659. }
  1660.  
  1661.  
  1662.  
  1663. /**********************************************************************/
  1664.  
  1665.  
  1666.  
  1667. #pragma segment TreeObj
  1668. void    UndoSwapChildren(TreeObjHndl undoPart)
  1669. {
  1670.     TreeObjHndl    shndl, dhndl, schndl, dchndl;
  1671.     short        scnum, dcnum;
  1672.  
  1673.     shndl = mDerefUndoPart(undoPart)->shndl;
  1674.     scnum = mDerefUndoPart(undoPart)->scnum;
  1675.     dhndl = mDerefUndoPart(undoPart)->dhndl;
  1676.     dcnum = mDerefUndoPart(undoPart)->dcnum;
  1677.  
  1678.     schndl = GetChildHndl(shndl, scnum);
  1679.     dchndl = GetChildHndl(dhndl, dcnum);
  1680.  
  1681.     DoTreeObjMethod(schndl, UNDOMESSAGE, UNDOFROMDOC);
  1682.     DoTreeObjMethod(dchndl, UNDOMESSAGE, UNDOFROMDOC);
  1683.  
  1684.     SwapChildren(NO_EDIT, shndl, scnum, dhndl, dcnum);
  1685.  
  1686.     DoTreeObjMethod(schndl, UNDOMESSAGE, UNDOTODOC);
  1687.     DoTreeObjMethod(dchndl, UNDOMESSAGE, UNDOTODOC);
  1688. }
  1689.  
  1690.  
  1691.  
  1692. /**********************************************************************/
  1693.  
  1694.  
  1695.  
  1696. /* Dispose of all undo information and prevent further undo collection.
  1697. ** Calling NewUndo() will re-enable undo collection. */
  1698.  
  1699. #pragma segment TreeObj
  1700. void    DisableUndo(TreeObjHndl hndl)
  1701. {
  1702.     TreeObjHndl    undo;
  1703.  
  1704.     undo = GetUndoHndl(hndl);
  1705.     while ((*undo)->numChildren) DisposeChild(NO_EDIT, undo, 0);
  1706.  
  1707.     mDerefUndo(undo)->undoDepth = 0;
  1708.     mDerefUndo(undo)->disabled  = true;
  1709. }
  1710.  
  1711.  
  1712.  
  1713. /**********************************************************************/
  1714.  
  1715.  
  1716.  
  1717. /* Dispose of all undo information and prevent further undo collection.
  1718. ** Calling NewUndo() will re-enable undo collection. */
  1719.  
  1720. #pragma segment TreeObj
  1721. void    DisposeUndos(TreeObjHndl hndl)
  1722. {
  1723.     DisableUndo(hndl);
  1724.     NewUndo(hndl);
  1725. }
  1726.  
  1727.  
  1728.  
  1729. /**********************************************************************/
  1730.  
  1731.  
  1732.  
  1733. /* Dispose of all undo information except the current undo.  The current undo
  1734. ** may still be active, and it may be needed to back out of an edit operation. */
  1735.  
  1736. #pragma segment TreeObj
  1737. Boolean    PurgeUndo(TreeObjHndl hndl)
  1738. {
  1739.     TreeObjHndl    undo;
  1740.     Boolean        didPurge;
  1741.  
  1742.     undo = GetUndoHndl(hndl);
  1743.     didPurge = false;
  1744.     while ((*undo)->numChildren > 1) {
  1745.         DisposeChild(NO_EDIT, undo, 0);
  1746.         didPurge = true;
  1747.     }
  1748.  
  1749.     return(didPurge);
  1750. }
  1751.  
  1752.  
  1753.  
  1754. /**********************************************************************/
  1755.  
  1756.  
  1757.  
  1758. #pragma segment TreeObj
  1759. void    GetUndoInfo(FileRecHndl frHndl, short *undoDepth, short *numUndos)
  1760. {
  1761.     TreeObjHndl    undo;
  1762.  
  1763.     *undoDepth = *numUndos = 0;
  1764.     if ((*frHndl)->fileState.readOnly) return;
  1765.  
  1766.     undo = GetUndoHndl((*frHndl)->d.doc.root);
  1767.     if (!mDerefUndo(undo)->disabled) {
  1768.         *undoDepth = mDerefUndo(undo)->undoDepth;
  1769.         *numUndos  = (*undo)->numChildren;
  1770.     }
  1771. }
  1772.  
  1773.  
  1774.  
  1775. /*****************************************************************************/
  1776. /*****************************************************************************/
  1777.  
  1778.  
  1779.  
  1780. /* This function does the standard document initialization. */
  1781.  
  1782. #pragma segment TreeObj
  1783. OSErr    DefaultInitDocument(FileRecHndl frHndl, short version, short numUndos, short numSaveUndos)
  1784. {
  1785.     TreeObjHndl    root, undo;
  1786.     OSErr        err;
  1787.  
  1788.     err = noErr;
  1789.  
  1790.     (*frHndl)->d.doc.fhInfo.version = version;
  1791.  
  1792.     (*frHndl)->fileState.readDocumentHeaderProc  = DefaultReadDocumentHeader;
  1793.     (*frHndl)->fileState.writeDocumentHeaderProc = DefaultWriteDocumentHeader;
  1794.  
  1795.     if (root = NewRootObj(ROOTOBJ, 0)) {        /* Create hierarchical data root. */
  1796.         (*frHndl)->d.doc.root = root;            /* Link file to hierarchical data. */
  1797.         undo = NewRootObj(UNDOOBJ, 0);            /* Create hierarchical undo root. */
  1798.         mDerefRoot(root)->undo    = undo;        /* Save hierarchical undo root. */
  1799.         mDerefRoot(root)->frHndl  = frHndl;
  1800.         if (undo) {
  1801.             (*frHndl)->fileState.defaultDoc = true;
  1802.             mDerefUndo(undo)->root   = root;    /* Point undo back at file root. */
  1803.             mDerefUndo(undo)->frHndl = frHndl;
  1804.             mDerefUndo(undo)->maxNumUndos  = numUndos;
  1805.             mDerefUndo(undo)->numSaveUndos = numSaveUndos;
  1806.         }
  1807.         else {
  1808.             DefaultFreeDocument(frHndl);
  1809.             err = memFullErr;
  1810.         }
  1811.     }
  1812.     else err = memFullErr;
  1813.  
  1814.     return(err);
  1815. }
  1816.  
  1817.  
  1818.  
  1819. /*****************************************************************************/
  1820.  
  1821.  
  1822.  
  1823. /* Frees up the hierarchical document and undo portions of a default document. */
  1824.  
  1825. #pragma segment TreeObj
  1826. OSErr    DefaultFreeDocument(FileRecHndl frHndl)
  1827. {
  1828.     TreeObjHndl    root, undo;
  1829.  
  1830.     if (root = (*frHndl)->d.doc.root) {            /* If we have a valid root object... */
  1831.         if (undo = mDerefRoot(root)->undo)        /* If we have a valid undo object... */
  1832.             DisposeObjAndOffspring(undo);        /* Dispose of undo info. */
  1833.         DisposeObjAndOffspring(root);            /* Dispose of hierarchical file data. */
  1834.         (*frHndl)->d.doc.root = nil;
  1835.     }
  1836.     return(noErr);
  1837. }
  1838.  
  1839.  
  1840.  
  1841. /*****************************************************************************/
  1842.  
  1843.  
  1844.  
  1845. #pragma segment TreeObj
  1846. OSErr    DefaultReadDocument(FileRecHndl frHndl)
  1847. {
  1848.     OSErr        err;
  1849.     TreeObjHndl    root, undo;
  1850.     short        resID, refNum, flags;
  1851.     Movie        movie;
  1852.     Boolean        wasChanged;
  1853.  
  1854.     refNum = (*frHndl)->fileState.refNum;
  1855.     err    = SetFPos(refNum, fsFromStart, 0);
  1856.         /* Set the file position to the beginning of the file. */
  1857.  
  1858.     err = DoReadDocumentHeader(frHndl);
  1859.  
  1860.     if (!err) {
  1861.         if ((*frHndl)->fileState.sfType == MovieFileType) {
  1862.             resID  = 0;
  1863.             flags  = (*frHndl)->fileState.movieFlags;
  1864.             err = NewMovieFromFile(&movie, refNum, &resID, nil, flags, &wasChanged);
  1865.             if (err) return(err);
  1866.             (*frHndl)->fileState.movie                  = movie;
  1867.             (*frHndl)->fileState.movieResID             = resID;
  1868.             (*frHndl)->fileState.movieDataRefWasChanged = wasChanged;
  1869.         }
  1870.         else {
  1871.             root = (*frHndl)->d.doc.root;        /* Preserve the undo field.  Preserving  */
  1872.             undo = mDerefRoot(root)->undo;        /* any application-sepcific fields is up */
  1873.                                                 /* to the application.                     */
  1874.             err = ReadTree(root, refNum);
  1875.                 /* Read in the hierarchical file data portion. */
  1876.  
  1877.             mDerefRoot(root)->undo   = undo;    /* Restore the 2 over-written fields. */
  1878.             mDerefRoot(root)->frHndl = frHndl;
  1879.         }
  1880.     }
  1881.  
  1882.     return(err);
  1883. }
  1884.  
  1885.  
  1886.  
  1887. /*****************************************************************************/
  1888.  
  1889.  
  1890.  
  1891. #pragma segment TreeObj
  1892. OSErr    DefaultReadDocumentFixup(FileRecHndl frHndl)
  1893. {
  1894.     TreeObjHndl    root, undo, chndl;
  1895.  
  1896.     root = (*frHndl)->d.doc.root;
  1897.     undo = mDerefRoot(root)->undo;
  1898.     for (;;) {
  1899.         if (!(chndl = GetChildHndl(root, -1))) break;
  1900.         if ((*chndl)->type != UNDOTASKOBJ)     break;
  1901.         MoveChild(NO_EDIT, root, -1, undo, -1);
  1902.         mDerefUndo(undo)->undoDepth++;
  1903.             /* Move any undo tasks that were saved with the document
  1904.             ** out of the document and into the undo hierarchy. */
  1905.     }
  1906.  
  1907.     return(noErr);
  1908. }
  1909.  
  1910.  
  1911.  
  1912. /*****************************************************************************/
  1913.  
  1914.  
  1915.  
  1916. #pragma segment TreeObj
  1917. OSErr    DefaultWriteDocument(FileRecHndl frHndl)
  1918. {
  1919.     short        refNum, cnum, numSaveUndos;
  1920.     OSErr        err;
  1921.     long        fpos;
  1922.     TreeObjHndl    root, undo, chndl;
  1923.  
  1924.     refNum = (*frHndl)->fileState.refNum;
  1925.     err    = DoWriteDocumentHeader(frHndl);
  1926.  
  1927.     if (!err) {
  1928.         if ((*frHndl)->fileState.sfType != MovieFileType) {
  1929.             undo         = GetUndoHndl(root = (*frHndl)->d.doc.root);
  1930.             numSaveUndos = mDerefUndo(undo)->numSaveUndos;
  1931.             for (cnum = mDerefUndo(undo)->undoDepth; ((cnum) && (numSaveUndos)); --numSaveUndos)
  1932.                 MoveChild(NO_EDIT, undo, --cnum, root, -1);
  1933.                     /* Move the designated number of undo tasks into the document side.
  1934.                     ** They will be saved to disk this way.  The designated number may be
  1935.                     ** zero, which means that undos aren't saved to disk. */
  1936.  
  1937.             DoNumberTree(root);
  1938.                 /* Assign each object in the tree a unique treeID.  This will allow
  1939.                 ** the objects to convert handle references into ID references so that
  1940.                 ** the data can be saved to disk. */
  1941.  
  1942.             err = WriteTree(root, refNum);
  1943.                 /* Write out the hierarchical data.  This includes the data in the root object.
  1944.                 ** When reading a data file, the root object data has already been initialized,
  1945.                 ** and therefore reading in the old root data from disk is a bad thing.  This is
  1946.                 ** handled by caching the root data prior to reading in a file, and then
  1947.                 ** restoring the data after the ReadTree() call has been made. */
  1948.  
  1949.             for (;;) {
  1950.                 if (!(chndl = GetChildHndl(root, -1))) break;
  1951.                 if ((*chndl)->type != UNDOTASKOBJ)     break;
  1952.                 MoveChild(NO_EDIT, root, -1, undo, cnum++);
  1953.                     /* Move any undo tasks that we previously moved into the document
  1954.                     ** out of the document and back into the undo hierarchy. */
  1955.             }
  1956.  
  1957.             if (!err) {
  1958.                 err = GetFPos(refNum, &fpos);
  1959.                 if (!err)
  1960.                     err = SetEOF(refNum, fpos);
  1961.             }            /* The document may be shorter than last time it was written to disk.
  1962.                         ** Handle this case by ending the file based on the new length. */
  1963.         }
  1964.         else {
  1965.         }
  1966.     }
  1967.  
  1968.     return(err);
  1969. }
  1970.  
  1971.  
  1972.  
  1973. /**********************************************************************/
  1974. /**********************************************************************/
  1975. /**********************************************************************/
  1976.  
  1977.  
  1978.  
  1979. #pragma segment TreeObj
  1980. OSErr    HReadTree(TreeObjHndl hndl, Handle tree)
  1981. {
  1982.     OSErr    err;
  1983.  
  1984.     if (!GetHandleSize(tree)) return(noErr);
  1985.  
  1986.     if (err = HReadBranch(hndl, tree)) {
  1987.         while ((*hndl)->numChildren)
  1988.             DisposeChild(NO_EDIT, hndl, 0);
  1989.     }
  1990.     else {
  1991.         DoNumberTree(hndl);
  1992.         DoFTreeMethod(hndl, CONVERTMESSAGE, CONVERTTOHNDL);
  1993.     }
  1994.  
  1995.     return(err);
  1996. }
  1997.  
  1998.  
  1999.  
  2000. /**********************************************************************/
  2001.  
  2002.  
  2003.  
  2004. /* This function recursively dissects the handle into separate tree objects.  The handle
  2005. ** has previously had tree objects streamed into it.  The public format is as follows:
  2006. ** 1) object header
  2007. ** 2) data length (4 bytes)
  2008. ** 3) data
  2009. **
  2010. ** After the object is created, the header is moved into it.  Then the data length is
  2011. ** fetched and a data handle is created to hold the data portion of the streamed data
  2012. ** for this object.  Once the data handle holds the object data, the tree handle has
  2013. ** the information for this object removed from it.
  2014. ** After this data separation, we call the object and pass it the data handle.
  2015. ** The object is responsible for interpreting the handle and initializing the data
  2016. ** portion of the object with it. */
  2017.  
  2018. #pragma segment TreeObj
  2019. OSErr    HReadBranch(TreeObjHndl hndl, Handle tree)
  2020. {
  2021.     TreeObjHndl        chndl;
  2022.     Handle            treeObjData;
  2023.     TreeObjProcPtr    proc;
  2024.     short            numChildren, cnum;
  2025.     long            size, diff;
  2026.     OSErr            err;
  2027.  
  2028.     if (GetHandleSize(tree) < sizeof(TreeObj)) return(paramErr);
  2029.  
  2030.     BlockMove(*tree, *hndl, sizeof(TreeObj) - sizeof(TreeObjHndl));
  2031.  
  2032.     numChildren = (*hndl)->numChildren;    /* So we can dispose of a partial read if */
  2033.     (*hndl)->numChildren = 0;            /* we have a failure after this point.    */
  2034.  
  2035.     BlockMove((long *)(*tree + sizeof(TreeObj) - sizeof(TreeObjHndl)), (Ptr)&size, sizeof(size));
  2036.         /* Get the size of the data following the header data. */
  2037.  
  2038.     if (!(treeObjData = NewHandle(size)))
  2039.         return(memFullErr);
  2040.             /* Create a handle the size of the data. */
  2041.  
  2042.     BlockMove(*tree + sizeof(TreeObj), *treeObjData, size);
  2043.         /* Copy the data into the handle. */
  2044.  
  2045.     diff = sizeof(TreeObj) + size;
  2046.     size = GetHandleSize(tree) - diff;
  2047.         /* Calculate how much we are going to shrink the tree handle. */
  2048.  
  2049.     BlockMove(*tree + diff, *tree, size);
  2050.     SetHandleSize(tree, size);
  2051.  
  2052.     if (proc = gTreeObjMethods[(*hndl)->type])
  2053.         err = (*proc)(hndl, HREADMESSAGE, (long)treeObjData);
  2054.     else
  2055.         err = HReadTreeObjData(hndl, treeObjData);
  2056.  
  2057.     DisposeHandle(treeObjData);
  2058.  
  2059.     if (err)
  2060.         return(err);
  2061.  
  2062.     for (cnum = 0; cnum < numChildren; ++cnum) {
  2063.         if (!(chndl = NewRootObj(EMPTYOBJ, 0))) return(memFullErr);
  2064.         if (InsertChildHndl(hndl, chndl, cnum)) {
  2065.             DisposeObjAndOffspring(chndl);
  2066.             return(memFullErr);
  2067.         }
  2068.         if (err = HReadBranch(chndl, tree))
  2069.             return(err);
  2070.     }
  2071.  
  2072.     return(noErr);
  2073. }
  2074.  
  2075.  
  2076.  
  2077. /**********************************************************************/
  2078.  
  2079.  
  2080.  
  2081. /* The simple handle read can assume that there will be no expansion or interpretation
  2082. ** of the data.  This means that the dataSize field represents the correct data size. */
  2083.  
  2084. #pragma segment TreeObj
  2085. OSErr    HReadTreeObjData(TreeObjHndl hndl, Handle treeObjData)
  2086. {
  2087.     long    dataSize;
  2088.     OSErr    err;
  2089.  
  2090.     if (!(err = SetDataSize(hndl, dataSize = (*hndl)->dataSize)))
  2091.         BlockMove(*treeObjData, (Ptr)(*hndl + 1), dataSize);
  2092.  
  2093.     return(err);
  2094. }
  2095.  
  2096.  
  2097.  
  2098. /**********************************************************************/
  2099.  
  2100.  
  2101.  
  2102. /* Given an open file that is ready to be written to, this function is called to
  2103. ** write the file tree to the designated file. */
  2104.  
  2105. #pragma segment TreeObj
  2106. OSErr    HWriteTree(TreeObjHndl hndl, Handle tree)
  2107. {
  2108.     TreeObjProcPtr    proc;
  2109.     Handle            data;
  2110.     short            cnum;
  2111.     OSErr            err;
  2112.     long            tsize, dsize;
  2113.  
  2114.     DoTreeObjMethod(hndl, CONVERTMESSAGE, CONVERTTOID);
  2115.         /* Ready data to be written to file.  Any references to handles are invalid
  2116.         ** when written to disk.  These need to be converted to a reference that
  2117.         ** makes sense when read in from disk when a file is opened.  The standard
  2118.         ** way to do this is to convert the handle reference to a tree-obj-number
  2119.         ** reference.  Prior to WriteTree() being called, DoNumberTree() is called.
  2120.         ** DoNumberTree() walks the tree and numbers each handle sequentially, thus
  2121.         ** giving each handle a unique id number. */
  2122.  
  2123.     if (!(data = NewHandle((*hndl)->dataSize)))
  2124.         return(memFullErr);
  2125.  
  2126.     if (proc = gTreeObjMethods[(*hndl)->type])
  2127.         err = (*proc)(hndl, HWRITEMESSAGE, (long)data);
  2128.     else
  2129.         err = HWriteTreeObjData(hndl, data);
  2130.  
  2131.     if (err) {
  2132.         DisposeHandle(data);
  2133.         return(err);
  2134.     }
  2135.  
  2136.     tsize = GetHandleSize(tree);
  2137.     dsize = GetHandleSize(data);
  2138.     SetHandleSize(tree, tsize + sizeof(TreeObj) + dsize);
  2139.     BlockMove((Ptr)*hndl,  (*tree + tsize), sizeof(TreeObj) - sizeof(TreeObjHndl));
  2140.     BlockMove((Ptr)&dsize, (*tree + tsize + sizeof(TreeObj) - sizeof(TreeObjHndl)), sizeof(long));
  2141.     BlockMove(*data,       (*tree + tsize + sizeof(TreeObj)), dsize);
  2142.  
  2143.     DisposeHandle(data);
  2144.     DoTreeObjMethod(hndl, CONVERTMESSAGE, CONVERTTOHNDL);
  2145.         /* Undo any id references back to handle references. */
  2146.  
  2147.     for (cnum = 0; cnum < (*hndl)->numChildren; ++cnum)
  2148.         if (err = HWriteTree(GetChildHndl(hndl, cnum), tree)) break;
  2149.  
  2150.     return(err);
  2151. }
  2152.  
  2153.  
  2154.  
  2155. /**********************************************************************/
  2156.  
  2157.  
  2158.  
  2159. /* Write out dataSize number of bytes from the object. */
  2160.  
  2161. #pragma segment TreeObj
  2162. OSErr    HWriteTreeObjData(TreeObjHndl hndl, Handle data)
  2163. {
  2164.     long    dataSize;
  2165.     OSErr    err;
  2166.  
  2167.     SetHandleSize(data, dataSize = (*hndl)->dataSize);
  2168.     if (err = MemError())
  2169.         return(err);
  2170.  
  2171.     BlockMove((Ptr)(*hndl + 1), *data, dataSize);
  2172.     return(noErr);
  2173. }
  2174.  
  2175.  
  2176.  
  2177. /**********************************************************************/
  2178. /**********************************************************************/
  2179.  
  2180.  
  2181.  
  2182. #pragma segment TreeObj
  2183. long    GetCData(TreeObjHndl hndl, long offset, char *data)
  2184. {
  2185.     Ptr        dptr;
  2186.     long    len;
  2187.  
  2188.     dptr = (Ptr)GetDataPtr(hndl) + offset;
  2189.     len  = clen(dptr);
  2190.  
  2191.     if (data)
  2192.         ccpy(data, dptr);
  2193.  
  2194.     return(len);
  2195. }
  2196.  
  2197.  
  2198.  
  2199. /**********************************************************************/
  2200.  
  2201.  
  2202.  
  2203. #pragma segment TreeObj
  2204. OSErr    PutCData(TreeObjHndl hndl, long offset, char *data)
  2205. {
  2206.     unsigned char    *dptr;
  2207.     long            oldl, newl;
  2208.     OSErr            err;
  2209.  
  2210.     dptr = (unsigned char *)GetDataPtr(hndl) + offset;
  2211.  
  2212.     oldl = clen((Ptr)dptr) + 1;
  2213.     newl = clen(data) + 1;
  2214.  
  2215.     if (err = SlideData(hndl, offset + oldl, newl - oldl)) return(err);
  2216.  
  2217.     dptr = (unsigned char *)GetDataPtr(hndl) + offset;
  2218.     ccpy((Ptr)dptr, data);
  2219.  
  2220.     return(noErr);
  2221. }
  2222.  
  2223.  
  2224.  
  2225. /**********************************************************************/
  2226.  
  2227.  
  2228.  
  2229. #pragma segment TreeObj
  2230. void    GetPData(TreeObjHndl hndl, long offset, StringPtr data)
  2231. {
  2232.     StringPtr    dptr;
  2233.  
  2234.     dptr = (StringPtr)GetDataPtr(hndl) + offset;
  2235.     pcpy(data, dptr);
  2236. }
  2237.  
  2238.  
  2239.  
  2240. /**********************************************************************/
  2241.  
  2242.  
  2243.  
  2244. #pragma segment TreeObj
  2245. OSErr    PutPData(TreeObjHndl hndl, long offset, StringPtr data)
  2246. {
  2247.     unsigned char    *dptr;
  2248.     long            oldl, newl;
  2249.     OSErr            err;
  2250.  
  2251.     dptr = (unsigned char *)GetDataPtr(hndl) + offset;
  2252.  
  2253.     oldl = *dptr + 1;
  2254.     newl = (*(unsigned char *)data) + 1;
  2255.  
  2256.     if (err = SlideData(hndl, offset + oldl, newl - oldl)) return(err);
  2257.  
  2258.     dptr = (unsigned char *)GetDataPtr(hndl) + offset;
  2259.     pcpy(dptr, data);
  2260.  
  2261.     return(noErr);
  2262. }
  2263.  
  2264.  
  2265.  
  2266. /**********************************************************************/
  2267.  
  2268.  
  2269.  
  2270. #pragma segment TreeObj
  2271. OSErr    PutShortData(TreeObjHndl hndl, long offset, void *data, unsigned short size)
  2272. {
  2273.     unsigned short    *dptr;
  2274.     long            oldl, newl;
  2275.     OSErr            err;
  2276.  
  2277.     dptr = (unsigned short *)((unsigned char *)GetDataPtr(hndl) + offset);
  2278.  
  2279.     oldl = *dptr + sizeof(short);
  2280.     newl = size  + sizeof(short);
  2281.  
  2282.     if (err = SlideData(hndl, offset + oldl, newl - oldl)) return(err);
  2283.  
  2284.     dptr = (unsigned short *)((unsigned char *)GetDataPtr(hndl) + offset);
  2285.     BlockMove(&size, dptr, sizeof(short));
  2286.     BlockMove(data, dptr + 1, size);
  2287.  
  2288.     return(noErr);
  2289. }
  2290.  
  2291.  
  2292.  
  2293. /**********************************************************************/
  2294.  
  2295.  
  2296.  
  2297. #pragma segment TreeObj
  2298. OSErr    PutLongData(TreeObjHndl hndl, long offset, void *data, long size)
  2299. {
  2300.     unsigned long    *dptr;
  2301.     long            oldl, newl;
  2302.     OSErr            err;
  2303.  
  2304.     dptr = (unsigned long *)((unsigned char *)GetDataPtr(hndl) + offset);
  2305.  
  2306.     oldl = *dptr + sizeof(long);
  2307.     newl = size  + sizeof(long);
  2308.  
  2309.     if (err = SlideData(hndl, offset + oldl, newl - oldl)) return(err);
  2310.  
  2311.     dptr = (unsigned long *)((unsigned char *)GetDataPtr(hndl) + offset);
  2312.     BlockMove(&size, dptr, sizeof(long));
  2313.     BlockMove(data, dptr + 1, size);
  2314.  
  2315.     return(noErr);
  2316. }
  2317.  
  2318.  
  2319.  
  2320. /**********************************************************************/
  2321.  
  2322.  
  2323.  
  2324. #pragma segment TreeObj
  2325. unsigned long    GetDataOffset(TreeObjHndl hndl, unsigned long offset, short dtype, short dnum)
  2326. {
  2327.     unsigned char    *dptr;
  2328.  
  2329.     while (dnum--) {
  2330.     dptr = (unsigned char *)GetDataPtr(hndl) + offset;
  2331.         switch (dtype) {
  2332.             case kCStr:
  2333.                 offset += clen((Ptr)dptr) + 1;
  2334.                 break;
  2335.             case kPStr:
  2336.                 offset += *dptr + 1;
  2337.                 break;
  2338.             case kSDataBlock:
  2339.                 offset += *(unsigned short *)dptr + sizeof(short);
  2340.                 break;
  2341.             case kLDataBlock:
  2342.                 offset += *(unsigned long *)dptr + sizeof(long);
  2343.                 break;
  2344.         }
  2345.     }
  2346.  
  2347.     return(offset);
  2348. }
  2349.  
  2350.  
  2351.  
  2352. /**********************************************************************/
  2353. /**********************************************************************/
  2354.  
  2355.  
  2356.  
  2357. #pragma segment TreeObj
  2358. Boolean    EqualTreeObjData(TreeObjHndl h1, TreeObjHndl h2)
  2359. {
  2360.     TreeObjProcPtr    proc;
  2361.  
  2362.     if ((h1) && (!h2)) return(false);
  2363.     if ((h2) && (!h1)) return(false);
  2364.  
  2365.     if ((*h1)->type != (*h2)->type) return(false);
  2366.  
  2367.     if (proc = gTreeObjMethods[(*h1)->type])
  2368.         return((*proc)(h1, COMPAREMESSAGE, (long)h2));
  2369.     else
  2370.         return(DefaultEqualTreeObjData(h1, h2));
  2371. }
  2372.  
  2373.  
  2374.  
  2375. /**********************************************************************/
  2376.  
  2377.  
  2378.  
  2379. #pragma segment TreeObj
  2380. Boolean    DefaultEqualTreeObjData(TreeObjHndl h1, TreeObjHndl h2)
  2381. {
  2382.     Ptr        p1, p2;
  2383.     long    ii, jj;
  2384.  
  2385.     if ((jj = (*h1)->dataSize) != (*h2)->dataSize) return(false);
  2386.  
  2387.     p1 = GetDataPtr(h1);
  2388.     p2 = GetDataPtr(h2);
  2389.  
  2390.     for (ii = 0; ii < jj; ++ii) {
  2391.         if (p1[ii] != p2[ii]) return(false);
  2392.     }
  2393.  
  2394.     return(true);
  2395. }
  2396.  
  2397.  
  2398.  
  2399.